﻿using AutoMapper;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Linq;
using Volo.Abp.Uow;
using YaAppoint.Appointments;
using YaAppoint.DapperRepositories;
using YaAppoint.ExamAppies;
using YaAppoint.OnlineServiceCache;
using YaAppoint.Response;
using YaAppoint.TextTemplates;
using YaAppoint.Users;

namespace YaAppoint.ExamApplications
{
    /// <summary>
    /// 检查申请
    /// </summary>
    [RemoteService(isEnabled: false)]
    public class ExamApplyAppService(IMapper mapper,
                                     ICurrentUserDepartmentAppService currentUserDepartmentAppService,
                                     IRepository<Department, Guid> depRepository,
                                     IRepository<TextTemplate, Guid> textTemplateRepository,
                                     IRepository<ExamApplication, Guid> examApplicationRepository,
                                     IApplySearchConfigService applySearchConfigService,
                                     IOnlineServiceCacheAppService onlineServiceCacheSerive,
                                     IDapperExamApplyItemRepository dapperExamApplyItemRepository,
                                     IRepository<DeviceType, Guid> deviceTypeRepository,
                                     IRepository<ApplyDepartment, Guid> applyDepartmentRepository,
                                     IRepository<ExamItem, Guid> examItemRepositroy,
                                     IUnitOfWorkManager unitOfWorkManager,
                                     AsyncQueryableExecuter queryableExecuter) : ApplicationService, IExamApplyAppService, ITransientDependency

    {
        private readonly ICurrentUserDepartmentAppService _currentUserDepartmentAppService = currentUserDepartmentAppService;
        private readonly IDapperExamApplyItemRepository _dapperExamApplyItemRepository = dapperExamApplyItemRepository;
        private readonly IRepository<TextTemplate, Guid> _textTemplateRepository = textTemplateRepository;
        private readonly IApplySearchConfigService _applySearchConfigService = applySearchConfigService;
        private readonly IRepository<ExamApplication, Guid> _examApplicationRepository = examApplicationRepository;
        private readonly IRepository<DeviceType, Guid> _deviceTypeRepository = deviceTypeRepository;
        private readonly IRepository<ApplyDepartment, Guid> _applyDepartmentRepository = applyDepartmentRepository;
        private readonly IRepository<ExamItem, Guid> _examItemRepositroy = examItemRepositroy;
        private readonly AsyncQueryableExecuter _queryableExecuter = queryableExecuter;
        private readonly IMapper _mapper = mapper;
        private readonly IUnitOfWorkManager _unitOfWorkManager = unitOfWorkManager;
        private readonly IRepository<Department, Guid> _depRepository = depRepository;
        private readonly IOnlineServiceCacheAppService _onlineServiceCacheService = onlineServiceCacheSerive;


        /// <summary>
        /// 获取申请项目
        /// </summary>
        /// <param name="number">输入的编号</param>
        /// <param name="userData">高级搜索过滤题条件</param>
        /// <param name="deviceTypeIds">设备类型</param>
        /// <returns></returns>
        public async Task<BackJson> GetApplyItemListAsync(string? number, Dictionary<string, object> userData, List<string> deviceTypeCodes, ApplyItemStatus status)
        {
            // 当前科室
            var currDep = _currentUserDepartmentAppService.GetCurrentUserDepartment();
            if (currDep == null || currDep["name"].Equals("永安医技预约") || string.IsNullOrEmpty(currDep["id"]))
            {
                throw new BusinessException(CustomErrorCodes.NotDepartmentUser);
            }

            // 配置his sql语句的在线服务
            var searchConfigTemplate = await _textTemplateRepository.FirstOrDefaultAsync(x => x.Interface!.Equals("IApplySearchConfigService") && x.IsActive);
            IApplySearchConfigService searchConfigService = null;
            if (searchConfigTemplate != null) // 存在活动的线上搜索配置服务
            {
                // 从缓存获取
                var compiledService = await _onlineServiceCacheService.GetOrAddOnlineService(_mapper.Map<TextTemplate, TextTemplateDto>(searchConfigTemplate));
                if (compiledService.GetInstance() is IApplySearchConfigService service)
                {
                    searchConfigService = service;
                }
                else
                {
                    throw new BusinessException(CustomErrorCodes.TemplateInterfaceUnimplemented)
                    {
                        Data = {
                            { "templateName",searchConfigTemplate.Name},
                            { "interface",searchConfigTemplate.Interface}
                        }
                    };
                }
            }
            else // 使用本地服务
            {
                searchConfigService = _applySearchConfigService;
            }

            // 用于额外映射的在线服务
            var mapServiceTemplate = await _textTemplateRepository.FirstOrDefaultAsync(t => t.Interface!.Equals("IApplyItemMapService") && t.IsActive);
            IApplyItemMapService mapService = null;
            if (mapServiceTemplate != null) // 存在活动的线上映射服务
            {
                // 从缓存获取
                var compiledService = await _onlineServiceCacheService.GetOrAddOnlineService(_mapper.Map<TextTemplate, TextTemplateDto>(mapServiceTemplate));
                if (compiledService.GetInstance() is IApplyItemMapService service)
                {
                    mapService = service;
                }
                else
                {
                    throw new BusinessException(CustomErrorCodes.TemplateInterfaceUnimplemented)
                    {
                        Data = {
                            {"templateName",mapServiceTemplate.Name},
                            {"interface",mapServiceTemplate.Interface}
                        }
                    };
                }
            }

            // 从pacs_view_exam_apply_item视图中查询 
            var viewList = await _dapperExamApplyItemRepository.GetExamApplyItemsAsync(number, userData, deviceTypeCodes, searchConfigService.GetSearchNumber(), searchConfigService.GetAddCondition());

            if (!viewList.Any())
            {
                return new BackJson()
                {
                    code = 0,
                    data = viewList
                };
            }

            // 提取所有 ExamItemNumber 
            var remoteExamItemNumbers = new HashSet<string>(viewList.Select(e => e.ExamItemNumber)!);

            // 批量查询本地库已有的 ExamItemNumber
            var existingExamItemNumbers = (await _examApplicationRepository.GetListAsync(e => remoteExamItemNumbers.Contains(e.ExamItemNumber)))
                                       .Select(e => e.ExamItemNumber)
                                       .ToHashSet();
            // 需要新建的
            var dtosToAdd = viewList.Where(e => !existingExamItemNumbers.Contains(e.ExamItemNumber!))
                                   .ToList();
            // 需要更新的
            var dtosToUpdate = viewList.Where(e => existingExamItemNumbers.Contains(e.ExamItemNumber!))
                                     .ToList();
            // 批处理新建与更新
            if (status == ApplyItemStatus.UnAppointed)
            {
                await ProcessEntitiesInBatches(dtosToAdd, dtosToUpdate, mapService, Guid.Parse(currDep["id"]));
            }
            var examItemNumberList = viewList.Select(e => e.ExamItemNumber).ToList();
            var query = await _examApplicationRepository.GetQueryableAsync();
            query = query.Where(ai => examItemNumberList.Contains(ai.ExamItemNumber));
            switch (status)
            {
                case ApplyItemStatus.UnAppointed: // 未预约
                    query = query.Where(ai => ai.AppointmentId == null);
                    break;
                case ApplyItemStatus.Appointed: // 已预约
                    query = query.Where(ai => ai.AppointmentId != null && ai.Appointment!.IsActive);
                    break;
                default:
                    break;
            }
            query = query.OrderBy(ai => ai.DeviceTypeId);
            var list = await _queryableExecuter.ToListAsync(query);

            return new BackJson()
            {
                code = 0,
                data = list.Select(i => new
                {
                    id = i.Id,
                    examItemName = i.ExamItemName,
                    patientId = i.PatientId,
                    deviceTypeId = i.DeviceTypeId,
                    deviceTypeName = i.DeviceType.Name,
                    applyTime = i.ApplyTime.ToString("MM-dd HH:ss"),
                    examItemNumber = i.ExamItemNumber,
                    workLoad = i.WorkLoad,
                    appointmentNumber = i.Appointment != null ? i.Appointment.Number : null,
                    appointmentStatus = i.Appointment != null ? Convert.ToInt32(i.Appointment.Status) : -1,
                    name = i.Name,
                    applyNumber = i.ApplyNumber,
                    appointment = i.AppointmentInfo,
                    oldInfo = i.AppointmentInfo,
                    oldPoolId = i.PoolId,
                    edited = false,
                    appointmentId = i.AppointmentId,
                    examItemFee = i.ExamItemFee != null ? i.ExamItemFee.Value.ToString("N2") : null,
                    poolId = i.PoolId
                })
            };
        }


        /// <summary>
        /// 获取申请单下所有检查项目
        /// </summary>
        public async Task<List<ExamApplicationDto>> GetApplyItemListAsync(string applyNumbers)
        {
            var viewList = await _dapperExamApplyItemRepository.GetExamApplyItemsAsync(applyNumbers.Split(',').ToList());
            if (!viewList.Any())
            {
                return new List<ExamApplicationDto>();
            }
            // 提取所有 ExamItemNumber 
            var remoteExamItemNumbers = new HashSet<string>(viewList.Select(e => e.ExamItemNumber)!);
            // 本地库已有的 ExamItemNumber
            var existingExamItemNumbers = (await _examApplicationRepository.GetListAsync(e => remoteExamItemNumbers.Contains(e.ExamItemNumber)))
                                            .Select(e => e.ExamItemNumber)
                                            .ToHashSet();
            // 需要新建的   
            var dtosToAdd = viewList.Where(e => !existingExamItemNumbers.Contains(e.ExamItemNumber!))
                            .ToList();
            // 需要更新的
            var dtosToUpdate = viewList.Where(e => existingExamItemNumbers.Contains(e.ExamItemNumber!))
                                .ToList();
            // 线上特殊映射服务
            var mapServiceTemplate = await _textTemplateRepository.FirstOrDefaultAsync(t => t.Interface!.Equals("IApplyItemMapService") && t.IsActive);
            IApplyItemMapService mapService = null;
            if (mapServiceTemplate != null)
            {
                // 从缓存中获取
                var compiledService = await _onlineServiceCacheService.GetOrAddOnlineService(_mapper.Map<TextTemplate, TextTemplateDto>(mapServiceTemplate));
                if (compiledService.GetInstance() is IApplyItemMapService service)
                {
                    mapService = service;
                }
                else
                {
                    throw new BusinessException(CustomErrorCodes.TemplateInterfaceUnimplemented)
                    {
                        Data = {
                            {"templateName",mapServiceTemplate.Name},
                            {"interface",mapServiceTemplate.Interface}
                        }
                    };
                }
            }
            // 批量新建与更新
            await AddAndUpdate(dtosToAdd, dtosToUpdate, mapService);
            // 本地查询申请项目
            var examItemNumberList = viewList.Select(e => e.ExamItemNumber).ToList();
            var query = await _examApplicationRepository.GetQueryableAsync();
            query = query.Where(ai => examItemNumberList.Contains(ai.ExamItemNumber));
            var list = await _queryableExecuter.ToListAsync(query);
            return _mapper.Map<List<ExamApplication>, List<ExamApplicationDto>>(list);
        }

        /// <summary>
        /// 同步申请单对应申请项目
        /// </summary>
        public async Task<BackJson> SyncApplyItemsAsync(string applyNumbers)
        {
            var breforeMemory = GC.GetTotalMemory(true);
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            var res = new BackJson();
            try
            {
                // 一次往返
                var viewList = await _dapperExamApplyItemRepository.GetExamApplyItemsAsync(applyNumbers.Split(',').ToList());
                if (!viewList.Any())
                {
                    res.code = 0;
                    res.msg = "无任何申请项目";
                    return res;
                }
                // 提取所有 ExamItemNumber 
                var remoteExamItemNubers = new HashSet<string>(viewList.Select(e => e.ExamItemNumber)!);
                // 本地库已有的 ExamItemNumber
                var existingExamItemNumbers = (await _examApplicationRepository.GetListAsync(e => remoteExamItemNubers.Contains(e.ExamItemNumber)))
                                                .Select(e => e.ExamItemNumber)
                                                .ToHashSet();
                // 需要新建的
                var dtosToAdd = viewList.Where(e => !existingExamItemNumbers.Contains(e.ExamItemNumber!))
                                .ToList();
                // 需要更新的
                var dtosToUpdate = viewList.Where(e => existingExamItemNumbers.Contains(e.ExamItemNumber!))
                                    .ToList();

                // 线上特殊映射服务
                var mapServiceTemplate = await _textTemplateRepository.FirstOrDefaultAsync(t => t.Interface!.Equals("IApplyItemMapService") && t.IsActive);
                IApplyItemMapService mapService = null;
                if (mapServiceTemplate != null)
                {
                    // 从缓存中获取
                    var compiledService = await _onlineServiceCacheService.GetOrAddOnlineService(_mapper.Map<TextTemplate, TextTemplateDto>(mapServiceTemplate));
                    if (compiledService.GetInstance() is IApplyItemMapService service)
                    {
                        mapService = service;
                    }
                    else
                    {
                        throw new BusinessException(CustomErrorCodes.TemplateInterfaceUnimplemented)
                        {
                            Data = {
                            {"templateName",mapServiceTemplate.Name},
                            {"interface",mapServiceTemplate.Interface}
                        }
                        };
                    }
                }
                // 批量新建与更新
                await AddAndUpdate(dtosToAdd, dtosToUpdate, mapService);
                res.code = 0;
                res.msg = "同步成功";
                Console.WriteLine($"memory---此函数一共消耗：{(GC.GetTotalMemory(true) - breforeMemory) / (1024 & 1024)}MB");
            }
            catch (Exception e)
            {
                res.code = 1;
                res.msg += e.Message;
            }
            
            return res;
        }

        /// <summary>
        /// 获取已预约/未预约申请项目
        /// </summary>
        /// <param name="applyNumbers"></param>
        /// <param name="type">"appointeds" 或 "noAppoints"</param>
        /// <returns></returns>
        public async Task<BackJson> GetApplyItemListAsync(string applyNumbers, ApplyItemStatus status)
        {
            var res = new BackJson();
            var numberList = applyNumbers.Split(',').ToList();
            var query = await _examApplicationRepository.GetQueryableAsync();
            query = query.Where(a => numberList.Contains(a.ApplyNumber));
            if (status == ApplyItemStatus.Appointed)
            {
                query = query.Where(a => a.AppointmentId != null);
            }
            else
            {
                query = query.Where(a => a.AppointmentId == null);
            }
            query = query.OrderBy(a => a.DeviceTypeId);
            var list = await _queryableExecuter.ToListAsync(query);

            return new BackJson()
            {
                code = 0,
                data = list.Select(i => new
                {
                    id = i.Id,
                    examItemName = i.ExamItemName,
                    patientId = i.PatientId,
                    deviceTypeId = i.DeviceTypeId,
                    deviceTypeName = i.DeviceType.Name,
                    applyTime = i.ApplyTime.ToString("MM-dd HH:ss"),
                    examItemNumber = i.ExamItemNumber,
                    workLoad = i.WorkLoad,
                    appointmentNumber = i.Appointment != null ? i.Appointment.Number : null,
                    appointmentStatus = i.Appointment != null ? Convert.ToInt32(i.Appointment.Status) : -1,
                    name = i.Name,
                    applyNumber = i.ApplyNumber,
                    appointment = i.AppointmentInfo,
                    oldInfo = i.AppointmentInfo,
                    oldPoolId = i.PoolId,
                    edited = false,
                    suggestionId="",
                    intervalId="",
                    appointmentId = i.AppointmentId,
                    examItemFee = i.ExamItemFee != null ? i.ExamItemFee.Value.ToString("N2") : null,
                    poolId = i.PoolId
                })
            };
        }

        /// <summary>
        /// 批处理
        /// 添加和更新申请单下申请项目
        /// </summary>
        /// <param name="toAdd">需要新增的检查项目</param>
        /// <param name="toUpdate">需要更新的检查项目</param>
        /// <param name="mapService">可能存在的线上映射服务</param>
        /// <returns></returns>
        private async Task AddAndUpdate(List<HisExamApplyItemDto> toAdd, List<HisExamApplyItemDto> toUpdate,IApplyItemMapService? mapService)
        {
            List<Dictionary<string, string>> examItemList = (await _examItemRepositroy.GetListAsync())
                                                               .Select(e => new Dictionary<string, string> {
                                                                    { "code",e.OuterCode!},
                                                                    { "id",e.Id.ToString()},
                                                                    { "workLoad",e.Workload.ToString()}
                                                               }).ToList();

            List<Dictionary<string, string>> applyDeplist = (await _applyDepartmentRepository.ToListAsync())
                                                             .Select(e => new Dictionary<string, string>{
                                                                    { "code",e.OuterCode},
                                                                    { "id",e.Id.ToString()}
                                                             }).ToList();

            List<Dictionary<string, string>> deviceTyleList = (await _deviceTypeRepository.GetListAsync())
                                                             .Select(e => new Dictionary<string, string> {
                                                                    { "code",e.OuterCode},
                                                                    { "id",e.Id.ToString()}
                                                             }).ToList();
            using (var uow = _unitOfWorkManager.Begin(requiresNew: true))
            {
                List<ExamApplication> addList = new List<ExamApplication>();
                // 处理新增记录
                foreach (var dto in toAdd)
                {
                    var entity = _mapper.Map<HisExamApplyItemDto, ExamApplication>(dto);
                    var applyDep = applyDeplist.FirstOrDefault(a => a["code"]!.Equals(entity.ApplyDepartmentCode));
                    if (applyDep == null)
                    {
                        throw new BusinessException(CustomErrorCodes.ApplyDepartmentNotExist);
                    }
                    var examItem = examItemList.FirstOrDefault(e => e["code"]!.Equals(entity.ExamItemCode));
                    if (examItem == null)
                    {
                        throw new BusinessException(CustomErrorCodes.ExamItemNotExist);
                    }
                    var deviceType = deviceTyleList.FirstOrDefault(e => e["code"]!.Equals(entity.DeviceTypeCode));
                    if (deviceTyleList == null)
                    {
                        throw new BusinessException(CustomErrorCodes.DeviceTypeNotExist);
                    }
                    entity.WorkLoad = Convert.ToInt32(examItem["workLoad"]);
                    entity.DeviceTypeId = Guid.Parse(deviceType!["id"]);
                    entity.ApplyDepartmentId = Guid.Parse(applyDep["id"]);
                    entity.ExamItemId = Guid.Parse(examItem["id"]);
                    addList.Add(entity);
                }

                List<ExamApplication> updateList = new List<ExamApplication>();
                var updateCodes = toUpdate.Select(e => e.ExamItemCode).ToList();
                var entities = await _examApplicationRepository.GetListAsync(e=> updateCodes.Contains(e.ExamItemCode));
                foreach (var dto in toUpdate)
                {
                    var entity = entities.FirstOrDefault(e=>e.ExamItemCode==dto.ExamItemCode);
                    if (entity==null)
                    {
                        continue;
                    }
                    // 映射
                    _mapper.Map<HisExamApplyItemDto, ExamApplication>(dto, entity);

                    // 特殊映射
                    if (mapService != null)
                    {
                        mapService.ExtraConvert(dto, entity);
                    }
                    updateList.Add(entity);
                }
                await _examApplicationRepository.InsertManyAsync(addList);
                await _examApplicationRepository.UpdateManyAsync(updateList);
                await uow.CompleteAsync();
            }
        }

        /// <summary>
        /// 批处理 
        /// </summary>
        /// <param name="toAdd"></param>
        /// <param name="toUpdate"></param>
        /// <returns></returns>
        private async Task ProcessEntitiesInBatches(List<HisExamApplyItemDto> toAdd, List<HisExamApplyItemDto> toUpdate, IApplyItemMapService? mapService, Guid depId)
        {
            var batchSize = 30; // 根据数据量和数据库性能来
            var department = await _depRepository.FirstOrDefaultAsync(e => e.Id == depId);
            if (department == null)
            {
                throw new BusinessException(CustomErrorCodes.DepartmentNotExist);
            }
            if (department.DeviceTypes == null || department.DeviceTypes.Count == 0)
            {
                throw new BusinessException(CustomErrorCodes.NoDeviceTypeExistInCurrentDep);
            }

            List<Dictionary<string, string>> examItemList = (await _examItemRepositroy.GetListAsync(e => e.DepartmentId == depId))
                                                                .Select(e => new Dictionary<string, string> {
                                                                    { "code",e.OuterCode!},
                                                                    { "id",e.Id.ToString()},
                                                                    { "workLoad",e.Workload.ToString()}
                                                                }).ToList();

            List<Dictionary<string, string>> applyDepList = (await _applyDepartmentRepository.ToListAsync())
                                                                .Select(e => new Dictionary<string, string>{
                                                                    { "code",e.OuterCode!},
                                                                    { "id",e.Id.ToString()}
                                                                }).ToList();
            List<Dictionary<string, string>> deviceTyleList = (await _deviceTypeRepository.GetListAsync(dt => department.DeviceTypes.Contains(dt)))
                                                                .Select(e => new Dictionary<string, string> {
                                                                    { "code",e.OuterCode!},
                                                                    { "id",e.Id.ToString()}
                                                                }).ToList();

            // 处理新增记录
            for (var i = 0; i < toAdd.Count; i += batchSize)
            {
                var batch = toAdd.Skip(i).Take(batchSize).ToList();
                using (var uow = _unitOfWorkManager.Begin(requiresNew: true))
                {
                    List<ExamApplication> addList = new List<ExamApplication>();
                    foreach (var dto in batch)
                    {
                        var entity = _mapper.Map<HisExamApplyItemDto, ExamApplication>(dto);
                        var applyDep = applyDepList.FirstOrDefault(a => a["code"]!.Equals(entity.ApplyDepartmentCode));
                        if (applyDep == null)
                        {
                            throw new BusinessException(CustomErrorCodes.ApplyDepartmentNotExist);
                        }
                        var examItem = examItemList.FirstOrDefault(e => e["code"]!.Equals(entity.ExamItemCode));
                        if (examItem == null)
                        {
                            throw new BusinessException(CustomErrorCodes.ExamItemNotExist);
                        }
                        var deviceType = deviceTyleList.FirstOrDefault(e => e["code"]!.Equals(entity.DeviceTypeCode));
                        if (deviceType == null)
                        {
                            throw new BusinessException(CustomErrorCodes.DeviceTypeNotExist);
                        }
                        entity.WorkLoad = Convert.ToInt32(examItem["workLoad"]);
                        entity.DeviceTypeId = Guid.Parse(deviceType["id"]);
                        entity.ApplyDepartmentId = Guid.Parse(applyDep["id"]);
                        entity.ExamItemId = Guid.Parse(examItem["id"]);
                        addList.Add(entity);
                    }
                    await _examApplicationRepository.InsertManyAsync(addList);
                    await uow.CompleteAsync();
                }
            }
                
            // 处理待更新记录
            for (var i = 0; i < toUpdate.Count; i += batchSize)
            {
                var batch = toUpdate.Skip(i).Take(batchSize).ToList();

                using (var uow = _unitOfWorkManager.Begin(requiresNew: true))
                {
                    List<ExamApplication> updateList = new List<ExamApplication>();
                    foreach (var dto in batch)
                    {
                        // 使用NoTracking查询获取实体（避免EF Core跟踪重复实体）
                        using (_examApplicationRepository.DisableTracking())  // 
                        {
                            var entity = await _examApplicationRepository.FirstOrDefaultAsync(e => e.ExamItemNumber == dto.ExamItemNumber);
                            if (entity != null)
                            {
                                // 映射
                                _mapper.Map<HisExamApplyItemDto, ExamApplication>(dto, entity);
                                if (mapService != null)
                                {
                                    mapService.ExtraConvert(dto, entity);
                                }
                                // 立刻保存修改到数据库
                                updateList.Add(entity);
                            }
                        }
                    }
                    await _examApplicationRepository.UpdateManyAsync(updateList);
                    await uow.CompleteAsync();
                }
            }
        }
    }
}
